﻿#region Namespace

//using DSO;
using ADODB;
using System;
using System.IO;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.Collections;
using System.Data.SqlClient;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using CommerceServer.Core.Runtime;
using CommerceServer.Core.Interop;
using CommerceServer.Core.Interop.Configuration;
using CommerceServer.Core.Runtime.Configuration;
using CS_MSCSCfg;
using System.Security.Principal;

#endregion Namespace

namespace CommerceServerTools
{

    #region Class DeleteSite

    /// <summary>
    /// This class is designed to delete a CS2K2 site.
    /// </summary>
    class DeleteSite
    {
        #region Private Variables

        /// <summary>
        /// ArrayList to hold all Commerce sites
        /// </summary>
        private static ArrayList CSSites;
        /// <summary>
        /// ArrayList to hold all Connection string to a Commerce site
        /// </summary>
        private ArrayList connectionsStr;
        /// <summary>
        /// ArrayList to hold all Commerce site's Databases
        /// </summary>
        private ArrayList databases;
        /// <summary>
        /// Commerce site naeme
        /// </summary>
        private string sitename;
        /// <summary>
        /// Global Config object
        /// </summary>
        private GlobalConfigFreeThreaded oGlbCfg = new GlobalConfigFreeThreaded();
        /// <summary>
        /// Site Config readonly object
        /// </summary>
        private SiteConfigReadOnlyFreeThreaded SiteCfg = new SiteConfigReadOnlyFreeThreaded();
        /// <summary>
        /// Site Config object
        /// </summary>
        private SiteConfigFreeThreaded siteConfig = new SiteConfigFreeThreaded();

        //private GlobalConfig oGlbCfg = new GlobalConfigClass();
        ///// <summary>
        ///// Site Config readonly object
        ///// </summary>
        //private SiteConfigReadOnly SiteCfg = new SiteConfigReadOnlyClass();
        ///// <summary>
        ///// Site Config object
        ///// </summary>
        //private SiteConfig siteConfig = new SiteConfigClass();


        #endregion Private Variables

        #region Main

        /// <summary>
        /// The main entry point for the DeleteCS2K2SIte. 
        /// If a site name is passed do a silent removal 
        /// otherwise display site available for selection.
        /// </summary>
        /// <param name="args">Commerce Server sitename</param>
        /// <example>
        /// <code lang="C#">DeleteCS2K2Site.exe sitename</code>
        /// </example>
        [STAThread]
        static void Main(string[] args)
        {
            // users selection for a silent removal
            string selection = null;

            // did we get any arguments?
            if (args.Length == 0)
            {
                Displaysites();
                selection = Console.ReadLine();

                while (selection != "")
                {
                    try
                    {
                        int arrayPos = Convert.ToInt16(selection);
                        selection = "";
                        Console.WriteLine();
                        Console.WriteLine("Deleting " + CSSites[arrayPos].ToString() + "...");

                        try
                        {
                            DeleteSite dsite = new DeleteSite(CSSites[arrayPos].ToString());
                            dsite.RemoveCSSite();
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                    }
                    catch (System.FormatException)
                    {
                        Console.WriteLine("\n(" + selection + ") does not correspond to a Commerce Site\n");
                        Displaysites();
                        selection = Console.ReadLine();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
            }
            else
            {
                selection = args[0].ToString();
                if (selection != null)
                {
                    try
                    {
                        DeleteSite dsite = new DeleteSite(selection);
                        dsite.RemoveCSSite();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
            }
        }// end main

        #endregion Main

        #region Private Methods

        /// <summary>
        /// Display all Commerce Site and allow user to select a single site.
        /// </summary>
        /// <example>
        /// <note type="note">This is a static method...</note>
        /// <code lang="C#">Displaysites();</code>
        /// </example>
        private static void Displaysites()
        {
            CSSites = new ArrayList();
            // If we didn't get a site name then get all Commerce Sites
            var oGlbCfg = new GlobalConfig2FreeThreaded();
            oGlbCfg.Initialize("");

            // Get all sites
            Recordset rst = (Recordset)oGlbCfg.Sites();
            int i = 0;

            // Dispaly a list of sites
            foreach (Field fld in rst.Fields)
            {
                i = CSSites.Add(fld.Value.ToString());
                Console.WriteLine("(" + i.ToString() + ") " + fld.Value.ToString());
            }
            Console.Write("Select a number for corresponding Commerce Site (or enter to quit): ");
        }// end Displaysites

        /// <summary>
        /// Get all Database Connection string for the selected Commerce site
        /// </summary>
        /// <example>
        /// <code lang="C#">GetConnectionStrings();</code>
        /// </example>
        private ArrayList GetConnectionStrings()
        {
            ArrayList siteConStrs = new ArrayList();
            string curfldName = null;
            string fldName = null;

            foreach (Field fld in SiteCfg.Fields)
            {
                // ignore dupe names
                fldName = fld.Name;
                Recordset rst = fld.Value as Recordset;
                // if we got a recordset then find out it we have a database
                if (rst != null)
                {
                    foreach (Field f in rst.Fields)
                    {
                        // determin if we have a connection string
                        if (f.Name.ToString().IndexOf("connstr_db", 0) == 0 || f.Name.ToString().IndexOf("s_BizDataStoreConnectionString") == 0)
                        {
                            string dbConStr = f.Value.ToString();
                            // strip out server name and database name from connection string
                            string dbName;
                            string dbServer;
                            GetDatabase(dbConStr, out dbName, out dbServer);
                            if (dbName != "")
                            {
                                // format all connection string to default
                                dbConStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=" + dbName + ";Data Source=" + dbServer + ";";
                                // search the array for the database name
                                if (siteConStrs.BinarySearch(dbConStr, Comparer.Default) < 0 && dbConStr != "")
                                {
                                    // the datawarehouse resource has two connection string we only need one
                                    // this works for any resource that may have two connection string
                                    //if (curfldName != fldName)
                                    //{
                                    siteConStrs.Add(dbConStr);
                                    // do a sort so our search will work
                                    siteConStrs.Sort();
                                    curfldName = fldName;
                                    //}
                                }
                            }
                        }
                    }
                }
            }
            Console.WriteLine("Retrieved all connection strings.");
            return siteConStrs;
        }// end GetConnectionStrings

        /// <summary>
        /// Strip out Server Name and Database Name from Connection string
        /// </summary>
        /// <param name="ConStr">An OleDb Connection string</param>
        /// <param name="dbName">Database Name</param>
        /// <param name="dbServer">Server Name</param>
        /// <example>
        /// <code lang="C#">
        /// string dbName;
        /// string dbServer;
        /// GetDatabase(dbConStr, out dbName, out dbServer);
        /// </code>
        /// </example>
        private void GetDatabase(string ConStr, out string dbName, out string dbServer)
        {
            // create a connection
            OleDbConnection Con = new OleDbConnection();

            // get the database name
            Con.ConnectionString = ConStr;
            dbName = Con.Database;
            dbServer = Con.DataSource;
        }// end GetDatabase

        /// <summary>
        /// Drop databases...
        /// <note type="note">Before dropping the databases we will kill all active connection...</note>
        /// </summary>
        /// <param name="dbName">Database Name</param>
        /// <param name="dbServer">Server Name</param>
        /// <example>
        /// <code lang="C#">DropCSDatabases(dbServer, dbName);</code>
        /// </example>
        private void DropCSDatabases(string dbServer, string dbName)
        {
            // don't delete predictor and direct mailer databases
            if (dbName != "DirectMailer" && dbName != "Predictor")
            {
                SqlConnection Con = null;
                SqlCommand Cmd = null;
                string SQLCmd = "DROP DATABASE " + dbName;

                Con = new SqlConnection("Data Source=" + dbServer + ";Initial Catalog=master;Integrated Security=SSPI");
                Con.Open();

                // kill all active connections
                KilActivelDBConnections(dbServer, dbName);

                try
                {
                    Cmd = new SqlCommand(SQLCmd, Con);
                    Cmd.CommandType = CommandType.Text;
                    Cmd.ExecuteReader();
                    Con.Close();
                    Console.WriteLine("\nDeleted the database " + dbName + ".");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }// end DropCSDatabases

        /// <summary>
        /// Delete Commerce Applications
        /// </summary>
        /// <example>
        ///<code lang="C#">DeleteApplications();</code>
        ///</example>
        private void DeleteApplications()
        {
            try
            {
                Console.WriteLine("\nDeleting the site\'s Applications...\n");

                foreach (Field fld in siteConfig.Fields)
                {
                    // only get recordsets
                    Recordset rst = fld.Value as Recordset;
                    if (rst != null)
                    {
                        // only deal with Applications
                        if (rst.Fields["s_ResourceType"].Value.ToString() == "Address")
                        {
                            System.Array WebServerMachine = (System.Array)siteConfig.MakeArrayFromString(rst.Fields["s_WebServerMachine"].Value.ToString());
                            System.Array WebServerInstance = (System.Array)siteConfig.MakeArrayFromString(rst.Fields["s_WebServerInstance"].Value.ToString());
                            string VirtualRootName = rst.Fields["s_VirtualRootName"].Value.ToString();

                            DeleteWebSite(WebServerMachine.GetValue(0).ToString(), WebServerInstance.GetValue(0).ToString(), VirtualRootName);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Unable to delete Application: " + e.Message);
            }
        }// end DeleteApplications

        /// <summary>
        /// Delete Commerce Web site
        /// </summary>
        /// <param name="Server">Server Name</param>
        /// <param name="IISInstance">IIS instance hosting Web</param>
        /// <param name="sPath">IIS Web application path</param>
        /// <example>
        /// <code lang="C#">DeleteWebSite(Server, IISInstance, sPath);</code>
        /// </example>
        private void DeleteWebSite(string Server, string IISInstance, string sPath)
        {
            bool Root = false;
            string sIISPath = @"IIS:\\" + Server + @"\W3SVC\" + IISInstance + @"\Root";

            object[] oParams = new object[2] { "IISWebVirtualDir", sPath };
            if (sPath == @"/")
            {
                sPath = "";
                Root = true;
            }
            else
            {
                sPath = @"\" + sPath;
            }

            // reset IIS before deleting
            IISReset(Server);

            DirectoryEntry oDE = new DirectoryEntry(sIISPath);
            DirectoryInfo dir = new DirectoryInfo(oDE.Properties["Path"].Value.ToString() + sPath);

            if (Root)
            {
                Console.WriteLine("Directory is root of IIS you must manually delete all files and folders...");
            }
            else
            {

                // delete the IIS directroy
                oDE.Invoke("Delete", oParams);

                try
                {
                    var sec = Directory.GetAccessControl(dir.FullName);
                    var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
                    sec.PurgeAccessRules(sid);

                    Directory.SetAccessControl(dir.FullName, sec);


                    Console.WriteLine("deleting (" + oDE.Properties["Path"].Value.ToString() + sPath + ") directory and it's files");

                    // delete the folder
                    dir.Delete(true);
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Problem deleting " + dir.FullName + " and its children :" + ex.Message);
                    Console.WriteLine("Continuing on, but you should investigate this and try to remove the directory yourself.");
                    Console.ForegroundColor = ConsoleColor.White;
                }
            }

        }// end DeleteWebSite

        /// <summary>
        /// Delete local Commerce resources
        /// </summary>
        /// <example>
        /// <code lang="C#">DeleteLocalResources();</code>
        /// </example>
        private void DeleteLocalResources()
        {
            try
            {
                Console.WriteLine("\nDeleting the site\'s local resources...\n");

                foreach (Field fld in siteConfig.Fields)
                {
                    if (fld.Value.GetType().IsCOMObject)
                    {
                        try
                        {
                            siteConfig.DeleteComponentConfig(fld.Name);
                            Console.WriteLine(String.Concat("Deleted resource ", fld.Name));
                        }
                        catch
                        {
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Unable to delete local resources: " + e.Message);
            }
        }// end DeleteLocalResources

        /// <summary>
        /// Delete global Commerce resources
        /// </summary>
        /// <example>
        /// <code lang="C#">DeleteGlobalResources();</code>
        /// </example>
        private void DeleteGlobalResources()
        {
            try
            {
                Console.WriteLine("\nDeleting the site\'s global resources...\n");

                var config2 = new GlobalConfig2FreeThreaded();
                var resources = config2.GetGlobalResources();

                config2.Initialize("");

                Fields globalResources2 = config2.Fields;

                foreach (Field globalResource in globalResources2)
                {
                    if (globalResource.Name.IndexOf(sitename) > -1)
                    {
                        var appName = globalResource.Name;

                        try
                        {
                            oGlbCfg.DeleteServiceConfig(appName);
                            Console.WriteLine(String.Concat("Deleted the global resource ", appName));
                        }
                        catch (Exception ex)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("Problem deleting global resource " + appName + " the issue was " + ex.Message);
                            Console.ForegroundColor = ConsoleColor.White;
                        }
                    }

                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Unable to delete global resources: " + e.Message);
            }
        }// end DeleteGlobalResources

        /// <summary>
        /// Delete Commerce Site
        /// </summary>
        /// <example>
        /// <code lang="C#">DeleteCSSite();</code>
        /// </example>
        private void DeleteCSSite()
        {
            try
            {
                Console.WriteLine("Deleting the site...\n");
                oGlbCfg.DeleteSiteConfig(sitename);
                Console.WriteLine("Deleted the site.");
            }
            catch (Exception e)
            {
                Console.WriteLine("Unable to delete the site: " + e.Message);
            }
        }// end DeleteCSSite

        /// <summary>
        /// Reset IIS...
        /// </summary>
        /// <note type="note">kill all active connection...</note>
        /// <param name="Server">Server Name</param>
        /// <example>
        ///<code lang="C#">
        ///IISReset(Server);
        ///</code>
        ///</example>
        private void IISReset(string Server)
        {
            Process IISProcess;
            string iisCmd = "/C iisreset " + Server;
            ProcessStartInfo startinfo = new ProcessStartInfo("cmd.exe");

            startinfo.Arguments = iisCmd;
            startinfo.RedirectStandardOutput = true;
            startinfo.UseShellExecute = false;

            IISProcess = Process.Start(startinfo);

            Console.Write(IISProcess.StandardOutput.ReadToEnd());

            IISProcess.WaitForExit();
        }// end IISReset

        /// <summary>
        /// kill all active connection...
        /// </summary>
        /// <param name="datasource">Server Name</param>
        /// <param name="DbName">Database Name</param>
        /// <example>
        /// <code lang="C#">KilActivelDBConnections(datasource, DbName);</code>
        /// </example>
        private void KilActivelDBConnections(string datasource, string DbName)
        {
            SqlConnection Con = null;
            SqlCommand Cmd = null;
            SqlDataReader DrSpid = null;
            SqlConnection Con2 = null;
            SqlDataReader DrKill = null;
            string SqlKill = "SELECT  spid FROM master.dbo.sysprocesses WHERE DB_NAME(dbid) = '" + DbName + "'";

            try
            {
                Con = new SqlConnection("Data Source=" + datasource + ";Initial Catalog=master;Integrated Security=SSPI");
                Con2 = new SqlConnection("Data Source=" + datasource + ";Initial Catalog=master;Integrated Security=SSPI");
                Con.Open();
                Con2.Open();
                Cmd = new SqlCommand(SqlKill, Con);
                Cmd.CommandType = CommandType.Text;
                DrSpid = Cmd.ExecuteReader();
                while (DrSpid.Read())
                {
                    try
                    {
                        Cmd = new SqlCommand("kill " + DrSpid.GetValue(0), Con2);
                        DrKill = Cmd.ExecuteReader();
                    }
                    catch (Exception e) { Console.WriteLine(e.Message); }
                    finally { DrKill.Close(); }
                }
            }
            catch (Exception e) { Console.WriteLine(e.Message); }
            finally { DrSpid.Close(); }
            Console.WriteLine("killed all active connection to Data Source=" + datasource + " Initial Catalog=" + DbName);
        }// end KilActivelDBConnections

        /// <summary>
        /// Release COM Object
        /// </summary>
        /// <example>
        /// <code lang="C#">ReleaseCOMObject(COMObject);</code>
        /// </example>
        private static void ReleaseCOMObject(object COMObject)
        {
            if (COMObject != null)
            {
                Marshal.ReleaseComObject(COMObject);
                GC.SuppressFinalize(COMObject);
            }
            COMObject = null;
        }// end ReleaseCOMObject

        #endregion Private Methods

        #region Public Methods

        /// <summary>
        /// Constructor for DeleteSite.
        /// </summary>
        /// <param name="args">Commerce Server sitename</param>
        /// <example>
        /// <code lang="C#">DeleteSite dsite = new DeleteSite(siteName);</code>
        /// </example>
        public DeleteSite(string siteName)
        {
            // initialize private variables
            this.databases = new ArrayList();
            this.sitename = siteName;
            this.connectionsStr = new ArrayList();
            this.oGlbCfg.Initialize(null);
            this.SiteCfg.Initialize(this.sitename);
            this.siteConfig.Initialize(sitename);
        }// end DeleteSite

        /// <summary>
        /// Remove selected Commerce site
        /// </summary>
        /// <example>
        /// <code lang="C#">RemoveCSSite();</code>
        /// </example>
        public void RemoveCSSite()
        {
            // get all Connection strings
            ArrayList ConStr = GetConnectionStrings();

            // delete all CS databases
            foreach (string str in ConStr)
            {
                string dbName;
                string dbServer;
                GetDatabase(str, out dbName, out dbServer);
                DropCSDatabases(dbServer, dbName);
            }

            // delete all aplications
            DeleteApplications();

            // delete site resources
            DeleteGlobalResources();
            DeleteLocalResources();

            // delete CS site
            DeleteCSSite();

            // house cleaning
            ReleaseCOMObject(oGlbCfg);
            ReleaseCOMObject(SiteCfg);
            ReleaseCOMObject(siteConfig);
        }// end RemoveCSSite

        #endregion Public Methods

    }// end class DeleteSite

    #endregion Class DeleteSite

}// End namespace DeleteCS2K2Site
